import { Task } from "../../app_base/module/task/Task";
import { IView } from "../../app_base/module/view/IView";
import { ViewConfig, IViewLoader } from "../../app_base/module/view/IViewLoader";
import { app } from "../../base";
import { ViewLevel } from "./Scene";
import { View } from "./View";

export type CCPViewConfig = {
    /**
     * 视图所需的附属资源，默认为空。
     */
    res?: string | string[],
    /**
     * 视图非必要的预加载资源，默认为空。只负责调起预加载，不保证加载成功。
     */
    preRes?: string | string[],
    /**
     * 挂载的View Class名称，默认为"View"。
     */
    viewClassName?: string,
    /**
     * 挂载的View Class。
     */
    viewClass?: { prototype: IView }
} & ViewConfig

/**
 * 注册View配置的装饰器(CCPViewLoader)
 * @param data 视图配置 CCPViewConfig=
 * @{
 * @ path 视图预制体路径
 * @ [name] 可选，视图名称, 默认为ViewClassName, 默认和视图预制体同名
 * @ [zIndex] 可选，视图显示层级, 默认为ViewLevel.UI
 * @ [viewClassName] 可选，视图根所挂载的ViewClass, 默认和视图预制体同名
 * @ [cache] 可选，界面是否缓存
 * @ [res] 可选，视图依赖的资源列表，默认为空
 * @ [preRes] 可选，视图加载完后发起的预加载资源列表，默认为空
 * @}
 * 
 * @example
 * ```
 * //通过装饰器注册视图
 * [@]app.viewClass({
        path: "Lv1://Prefab/Lv", cache: false, zIndex: app.ViewLevel.UI, viewClassName: "Lv1",
        //加载视图时，同时加载的依赖资源
        //res:[], 
        //加载视图后，发起的预加载资源
        //preRes:[]
    })
    export class Lv1 extends app.View {
    }

    //注册Lv1的class（同一个class）到Lv101视图
    app.viewClass({path: "Lv101://Prefab/Lv", cache: false, zIndex: app.ViewLevel.UI, viewClassName: "Lv1", name: "Lv101"})(Lv1);
    //注册Lv1的class（同一个class）到Lv102视图
    app.viewClass({path: "Lv102://Prefab/Lv", cache: false, zIndex: app.ViewLevel.UI, viewClassName: "Lv1", name: "Lv102"})(Lv1);
    * ```
    */
export function viewClass(data: CCPViewConfig): any {
    return function (target: { prototype: IView }, propertyKey: string) {
        data.zIndex = data.zIndex || ViewLevel.UI;
        data.viewClassName = data.viewClassName || data.path.slice(data.path.lastIndexOf("/") + 1);// || target['name']
        data.name = data.name || data.viewClassName;
        data.loaderType = CCPViewLoader.TYPE;
        data.viewClass = target;
        app.viewManager.registerView(data.name, data);
    }
}

/**
 * cocos 默认视图加载器（cc.Prefab）
 */
export class CCPViewLoader extends IViewLoader {
    protected _asset: cc.Prefab;
    public static TYPE: string = "CCPViewLoader";

    /**
     * 创建CCPViewLoader的View配置
     * @param data 视图配置 CCPViewConfig=
     * @{
     * @ path 视图预制体路径
     * @ [name] 可选，视图名称, 默认为ViewClassName, 默认和视图预制体同名
     * @ [zIndex] 可选，视图显示层级, 默认为ViewLevel.UI
     * @ [viewClassName] 可选，视图根所挂载的ViewClass, 默认和视图预制体同名
     * @ [res] 可选，视图依赖的资源列表，默认为空
     * @ [preRes] 可选，视图加载完后发起的预加载资源列表，默认为空
     * @ [cache] 可选，界面是否缓存
     * @}
     */
    public static createConfig(data: CCPViewConfig): CCPViewConfig {
        data.zIndex = data.zIndex || ViewLevel.UI;
        data.viewClassName = data.viewClassName || data.path.slice(data.path.lastIndexOf("/") + 1);
        data.name = data.name || data.viewClassName;
        data.loaderType = CCPViewLoader.TYPE;
        return data;
    }

    protected _config: CCPViewConfig;
    protected _viewBundle: cc.AssetManager.Bundle;

    init(config: CCPViewConfig): CCPViewLoader {
        this._config = config;
        console.time(`[加载View]${config.viewClassName}`);
        /**
         * 加载视图必要资源
         */
        config.res && config.res.length > 0 && this._initializer.addQuickly(this, this._loadRes, config);
        /**
         * 加载视图本身
         */
        this._initializer.addQuickly(this, this._loadView, config);
        /**
         * 预加载额外资源
         */
        config.preRes && config.preRes.length > 0 && app.res.preload(config.preRes);
        return this;
    }

    /**
     * 加载视图预制体
     * @param task 
     * @param config 
     */
    protected _loadView(task: Task, config: CCPViewConfig) {
        app.res.load<cc.Prefab>(config.path).then((assets) => {
            if (!assets) {
                task.onError("loadView", `加载界面${config.path}发生错误`);
                console.error("View_loadView", `加载界面${config.path}发生错误`);
                return;
            }
            this._asset = assets;
            // this._asset.addRef();
            this._viewBundle = cc.assetManager.getBundle(app.res.parsePath(config.path).bundle);
            console.timeEnd(`[加载View]${config.viewClassName}`);
            task.onComplete();
        })
    }

    /**
     * 加载视图所需的附属资源
     * @param task 
     * @param config 
     */
    protected _loadRes(task: Task, config: CCPViewConfig) {
        if (!config.res || config.res.length <= 0) {
            task.onComplete();
            return;
        }
        app.res.load(config.res).then((value) => {
            if (!value) {
                task.onError("CCPViewLoader.loadRes", `加载视图资源出错${config.res}`);
                return;
            }
            task.onComplete();
        })
    }

    initView<T extends IView>(viewName: string, recoverLoader?: boolean): T {
        if (this._asset && cc.isValid(this._asset)) {
            let node = cc.instantiate(this._asset)
            let script: any = node.getComponent(this._config.viewClassName || View.type);
            if (!script) {
                // throw new Error(`未获取到${this._config.path}中的${this._config.viewClassName}挂载脚本`);
                script = node.addComponent(this._config.viewClassName)
            }
            script.bundle = this._viewBundle;//方便view后续逻辑。
            if (recoverLoader) {
                this.recover();
            }
            return script;
        }
        throw new Error(`${this._config.path}视图素材无效：${cc.isValid(this._asset)}, View初始化失败`);
    }

    public reset(): void {
        this._viewBundle = null;
        this._config = null;
        // this._asset && this._asset.decRef();
        this._asset = null;
        super.reset();
    }

    public destroy(): void {
        this._config = null;
        this._viewBundle = null;
        this._asset && this._asset.decRef();
        this._asset && cc.assetManager.releaseAsset(this._asset);
        this._asset = null;
        super.destroy();
    }

}
